/* not type checking this file because flow doesn't play well with Proxy */

import config from 'core/config';
import { warn, makeMap, isNative } from '../util/index';

let initProxy;

if (process.env.NODE_ENV !== 'production') {
    const allowedGlobals = makeMap(
        'Infinity,undefined,NaN,isFinite,isNaN,' +
            'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' +
            'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt,' +
            'require' // for Webpack/Browserify
    );

    const warnNonPresent = (target, key) => {
        warn(
            `Property or method "${key}" is not defined on the instance but ` +
                'referenced during render. Make sure that this property is reactive, ' +
                'either in the data option, or for class-based components, by ' +
                'initializing the property. ' +
                'See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.',
            target
        );
    };

    const warnReservedPrefix = (target, key) => {
        warn(
            `Property "${key}" must be accessed with "$data.${key}" because ` +
                'properties starting with "$" or "_" are not proxied in the Vue instance to ' +
                'prevent conflicts with Vue internals. ' +
                'See: https://vuejs.org/v2/api/#data',
            target
        );
    };

    const hasProxy = typeof Proxy !== 'undefined' && isNative(Proxy);

    if (hasProxy) {
        const isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta,exact');
        config.keyCodes = new Proxy(config.keyCodes, {
            set(target, key, value) {
                if (isBuiltInModifier(key)) {
                    warn(`Avoid overwriting built-in modifier in config.keyCodes: .${key}`);
                    return false;
                } else {
                    target[key] = value;
                    return true;
                }
            }
        });
    }

    const hasHandler = {
        has(target, key) {
            const has = key in target;
            const isAllowed =
                allowedGlobals(key) ||
                (typeof key === 'string' && key.charAt(0) === '_' && !(key in target.$data));
            if (!has && !isAllowed) {
                if (key in target.$data) warnReservedPrefix(target, key);
                else warnNonPresent(target, key);
            }
            return has || !isAllowed;
        }
    };

    const getHandler = {
        get(target, key) {
            if (typeof key === 'string' && !(key in target)) {
                if (key in target.$data) warnReservedPrefix(target, key);
                else warnNonPresent(target, key);
            }
            return target[key];
        }
    };

    initProxy = function initProxy(vm) {
        // 判断当前环境是否有Proxy对象
        if (hasProxy) {
            // determine which proxy handler to use
            const options = vm.$options;
            const handlers =
                options.render && options.render._withStripped ? getHandler : hasHandler;
            vm._renderProxy = new Proxy(vm, handlers);
        } else {
            vm._renderProxy = vm;
        }
    };
}

export { initProxy };
